package top.kaoshanji.p1ch06a;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;
import java.util.stream.Collectors;

import static top.kaoshanji.p1ch06a.Dish.dishTags;
import static top.kaoshanji.p1ch06a.Dish.menu;

/**
 * author kaoshanji
 * ^_^
 * create time 2025/2/12 9:15
 */
public class Grouping {

    private static final Logger logger = LogManager.getLogger(Grouping.class);

    public static void main(String[] args) {

        // 代码清单-p1ch06a-127页a：把菜单中的菜按照类型进行分类
        logger.info("...把菜单中的菜按照类型进行分类...");
        System.out.println(groupDishesByType());


        // 代码清单-p1ch06a-128页a：把菜单中的菜按照热量进行分类
        logger.info("...把菜单中的菜按照热量进行分类...");
        System.out.println(groupDishesByCaloricLevel());


        // 代码清单-p1ch06a-129页a：只按照菜肴的热量进行过滤
        logger.info("...只按照菜肴的热量进行过滤...");
        System.out.println(groupCaloricDishesByType());


        // 代码清单-p1ch06a-129页b：将每道菜肴的分类添加到它们各自的菜名中
        logger.info("...将每道菜肴的分类添加到它们各自的菜名中...");
        System.out.println(groupDishNamesByType());


        // 代码清单-p1ch06a-130页a：提取出每组菜肴对应的标签
        logger.info("...提取出每组菜肴对应的标签...");
        System.out.println(groupDishTagsByType());


        // 代码清单-p1ch06a-130页b：多级分组
        logger.info("...多级分组...");
        System.out.println(groupDishedByTypeAndCaloricLevel());


        // 代码清单-p1ch06a-131页a：数一数菜单中每类菜有多少个
        logger.info("...数一数菜单中每类菜有多少个...");
        System.out.println(countDishesInGroups());


        // 代码清单-p1ch06a-132页a：修改前面查找菜单中热量最高的菜肴的收集器
        logger.info("...修改前面查找菜单中热量最高的菜肴的收集器...");
        Map<Dish.Type, Optional<Dish>> mostCaloricByType = menu.stream().collect(Collectors.groupingBy(
                Dish::getType, Collectors.maxBy(Comparator.comparingInt(Dish::getCalories))));
        System.out.println(mostCaloricByType);


        // 代码清单-p1ch06a-132页b：查找每个子组中热量最高的Dish
        logger.info("...查找每个子组中热量最高的Dish...");
        Map<Dish.Type, Dish> mostCaloricByType1 = menu.stream().collect(Collectors.groupingBy(
                Dish::getType, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories))
                , Optional::get)));
        System.out.println(mostCaloricByType1);


        // 代码清单-p1ch06a-134页a：每组菜肴热量的总和
        logger.info("...每组菜肴热量的总和...");
        System.out.println(sumCaloriesByType());

    }

    enum CaloricLevel { DIET, NORMAL, FAT };

    private static Map<Dish.Type, List<Dish>> groupDishesByType() {
        return menu.stream().collect(Collectors.groupingBy(Dish::getType));
    }

    private static Map<CaloricLevel, List<Dish>> groupDishesByCaloricLevel() {
        return menu.stream().collect(
                Collectors.groupingBy(dish -> {
                    if (dish.getCalories() <= 400) {
                        return CaloricLevel.DIET;
                    } else if (dish.getCalories() <= 700) {
                        return CaloricLevel.NORMAL;
                    } else {
                        return CaloricLevel.FAT;
                    }
                })
        );
    }

    private static Map<Dish.Type, List<Dish>> groupCaloricDishesByType() {
        return menu.stream().collect(
                Collectors.groupingBy(Dish::getType, Collectors.filtering(
                        dish -> dish.getCalories() > 500, Collectors.toList()))
        );
    }

    private static Map<Dish.Type, List<String>> groupDishNamesByType() {
        return menu.stream().collect(
                Collectors.groupingBy(Dish::getType, Collectors.mapping(Dish::getName, Collectors.toList())));
    }

    private static Map<Dish.Type, Set<String>> groupDishTagsByType() {
        return menu.stream().collect(
                Collectors.groupingBy(Dish::getType,
                        Collectors.flatMapping(dish -> dishTags.get(dish.getName()).stream(), Collectors.toSet())));
    }

    private static Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishedByTypeAndCaloricLevel() {
        return menu.stream().collect(
                Collectors.groupingBy(Dish::getType,
                        Collectors.groupingBy((Dish dish) -> {
                            if (dish.getCalories() <= 400) {
                                return CaloricLevel.DIET;
                            }
                            else if (dish.getCalories() <= 700) {
                                return CaloricLevel.NORMAL;
                            }
                            else {
                                return CaloricLevel.FAT;
                            }
                        })
                )
        );
    }


    private static Map<Dish.Type, Long> countDishesInGroups() {
        return menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));
    }

    private static Map<Dish.Type, Integer> sumCaloriesByType() {
        return menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.summingInt(Dish::getCalories)));
    }




}
